﻿using NhWeather.Properties;
using NhWeather.Register;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace NhWeather.Modbus;

/// <summary>
/// 返回帧
/// </summary>
public abstract class AcceptCmd : BaseFrame
{

    /// <summary>
    /// 组成完整帧，气象数据返回
    /// </summary>
    /// <param name="cmdData">命令中数据</param>
    /// <param name="Registers">寄存器组</param>
    /// <param name="deviceAddress">设备地址</param>
    /// <returns></returns>
    public abstract byte[] GetFrame(byte[] cmdData, ObservableCollection<BaseRegister> Registers, byte deviceAddress);
}

/// <summary>
/// 读寄存器，实时数据
/// </summary>
public class ReadRegCmd : AcceptCmd
{
    public ReadRegCmd()
    {
        Code = 0x04;
    }

    public override byte[] GetFrame(byte[] cmdData, ObservableCollection<BaseRegister> Registers, byte deviceAddress)
    {
        int dataLength = cmdData[3] << 1;//返回帧长度保存在一个字节内，这里高位无意义
        int startReg = ((UInt16)cmdData[0] << 8) + cmdData[1];
        byte[] frame = new byte[dataLength + 5];
        frame[0] = deviceAddress;
        frame[1] = Code;
        frame[2] = (byte)dataLength;
        for (int i = 0; i < dataLength; i++)
        {
            var reg = Registers.SingleOrDefault(r => r.RegAddress == startReg + (i >> 1));
            if (reg == null &&
                ((Code == 3 && Settings.Default.IsAutoAdd03Reg) ||
                (Code == 4 && Settings.Default.IsAutoAdd04Reg)))
            {   // 新增对应的 Register
                if (Code == 3)
                {
                    reg = new HoldingRegister()
                    {
                        RegAddress = (ushort)(startReg + (i >> 1))
                    };
                }
                else
                {
                    reg = new SensorRegister(2000, 0)
                    {
                        RegAddress = (ushort)(startReg + (i >> 1))
                    };
                }
                Application.Current.Dispatcher.Invoke(() =>
                {   // 使用 Dispatcher 调度到 UI 线程
                    Registers.Add(reg);
                });
            }
            short data = reg?.Data ?? short.MinValue;
            frame[3 + i] = (byte)(data >> 8);
            i++;
            frame[3 + i] = (byte)data;
        }
        AddCrc(ref frame);

        return frame;
    }
}

/// <summary>
/// 读保存寄存器，实时数据
/// </summary>
public class ReadHoldRegCmd : ReadRegCmd
{
    public ReadHoldRegCmd()
    {
        Code = 0x03;
    }
}

/// <summary>
/// 读历史数据
/// </summary>
public class ReadHistoryCmd : AcceptCmd
{
    public static int MaxHistoryData { get; set; } = 100;

    public ReadHistoryCmd()
    {
        Code = 0x41;
    }

    public override byte[] GetFrame(byte[] cmdData, ObservableCollection<BaseRegister> Registers, byte deviceAddress)
    {

        byte[] frame = new byte[54 + 5];
        frame[0] = deviceAddress;
        frame[1] = Code;
        frame[2] = 54;
        DateTime time = DateTime.Now;
        //根据命令数据时间前推
        uint temp = 0;
        for (int i = 0; i < 4; i++)
        {
            temp <<= 8;
            temp |= cmdData[i];
        }

        if (temp < MaxHistoryData)
        {
            time = time.AddMinutes(-temp * 10);

            frame[3] = (byte)(time.Year - 2000);
            frame[4] = (byte)time.Month;
            frame[5] = (byte)time.Day;
            frame[6] = (byte)time.Hour;
            frame[7] = (byte)((time.Minute / 10) * 10);//时间取整10分钟
            frame[8] = 0;
        }
        else
        {
            for (int i = 0; i < 6; i++)
                frame[3 + i] = 0xFF;
        }

        for (int i = 0; i < 48; i++)
        {
            short data = Registers.SingleOrDefault(r => r.RegAddress == 3 + (i >> 1))?.Data ?? short.MinValue;
            frame[9 + i] = (byte)(data >> 8);
            i++;
            frame[9 + i] = (byte)data;
        }

        AddCrc(ref frame);

        return frame;
    }
}

/// <summary>
/// 写单个寄存器
/// </summary>
public class WriteReg : AcceptCmd
{
    public WriteReg()
    {
        Code = 0x06;
    }

    public override byte[] GetFrame(byte[] cmdData, ObservableCollection<BaseRegister> Registers, byte deviceAddress)
    {
        byte[] frame = null;

        ushort regAddress = (ushort)((cmdData[0] << 8) + cmdData[1]);
        short value = (short)((cmdData[2] << 8) + cmdData[3]);

        var reg = Registers.SingleOrDefault(r => r.RegAddress == regAddress);
        if (reg == null)
        {
            if (Settings.Default.IsAutoAdd06Reg)
            {   // 新增对应的 Register                
                reg = new HoldingRegister()
                {
                    RegAddress = regAddress,
                };
                Application.Current.Dispatcher.Invoke(() =>
                {
                    Registers.Add(reg);
                });
            }
            else
            {   //非法，返回错误帧
                frame = [deviceAddress, (byte)(Code | 0x80), 2, 0, 0];
                AddCrc(ref frame);
                return frame;
            }
        }
        reg.Data = value;

        frame = [deviceAddress, Code, cmdData[0], cmdData[1], cmdData[2], cmdData[3], 0, 0];

        AddCrc(ref frame);
        return frame;
    }
}

/// <summary>
/// 写多个寄存器，校时
/// </summary>
public class WriteMultReg : AcceptCmd
{
    public WriteMultReg()
    {
        Code = 0x10;
    }

    public override byte[] GetFrame(byte[] cmdData, ObservableCollection<BaseRegister> Registers, byte deviceAddress)
    {
        byte[] frame = null;

        ushort startReg = (ushort)((cmdData[0] << 8) + cmdData[1]);
        ushort regCount = cmdData[3];//返回帧长度保存在一个字节内，这里高位无意义
        byte byteCount = cmdData[4];
        if (cmdData[2] != 0 || regCount * 2 != byteCount || regCount == 0)
        {   //非法，返回错误帧
            frame = [deviceAddress, (byte)(Code | 0x80), 3, 0, 0];
        }
        else
        {
            for (int i = 0; i < regCount; i++)
            {
                short data = (short)((cmdData[5 + i * 2] << 8) + cmdData[6 + i * 2]);
                var reg = Registers.SingleOrDefault(r => r.RegAddress == startReg + i);
                if (reg == null)
                {
                    if (Settings.Default.IsAutoAdd16Reg)
                    {   // 新增对应的 Register
                        reg = new HoldingRegister()
                        {
                            RegAddress = (ushort)(startReg + i)
                        };
                        Application.Current.Dispatcher.Invoke(() =>
                        {
                            Registers.Add(reg);
                        });
                    }
                    else
                    {   //非法，返回错误帧
                        frame = [deviceAddress, (byte)(Code | 0x80), 2, 0, 0];
                        AddCrc(ref frame);
                        return frame;
                    }
                }
                reg.Data = data;
            }

            frame = [deviceAddress, Code, (byte)(startReg >> 8), (byte)startReg, 0, 3, 0, 0];
        }
        AddCrc(ref frame);
        return frame;
    }
}

